package com.morningstar.qs.api.basic.common.date;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;


public class DateFrequencyProcess {
	//private static Logger gLogger = Logger.getLogger(DateFrequencyProcess.class);
	
	public final static int DEFAULT_DAYS_FROM_19000101 = 25567;
	
	public static Date getLastDayOfMonth(Date date) {
		if (null == date) {
			return null;
		}
		
		Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.set(Calendar.DAY_OF_MONTH, 1);
        cal.roll(Calendar.DAY_OF_MONTH, -1);
        return cal.getTime();
	}
	
	public static Date getFridayOfWeek(Date date) {
		if (null == date) {
			return null;
		}
		
		Calendar cal = Calendar.getInstance();
        cal.setFirstDayOfWeek(Calendar.MONDAY);
        cal.setTime(date);
        cal.set(Calendar.DAY_OF_WEEK, cal.getFirstDayOfWeek() + 4);
        return cal.getTime();
	}
	
	/**
	 * return workday between start date and end date, 
	 * and if haveHolidays is set to true, holiday will also be filtered out.
	 * 
	 * @param start
	 * @param end
	 * @param haveHolidays
	 * @param country
	 * @return
	 */
	public static List<Date> getDailyDate(String start, String end, boolean haveHolidays, String country) {
		if (null == start || 0 == start.length() 
				|| null == end || 0 == end.length()) {
			return null;
		}
		
		if (haveHolidays) {
			if (null == country || 0 == country.length()) {
				return null;
			}
		}
		
		Date endDate = null;
		Date startDate = null;
		try {
			startDate = new SimpleDateFormat("yyyy-MM-dd").parse(start);
			endDate = new SimpleDateFormat("yyyy-MM-dd").parse(end);
		} catch (ParseException e) {
			e.printStackTrace();
		}
		
		if (null == startDate || null == endDate || startDate.getTime() > endDate.getTime()) {
			return null;
		}
		
		List<Date> lstDate = new ArrayList<Date>();
		while (startDate.getTime() <= endDate.getTime()) {
			Calendar startCal = Calendar.getInstance();
			startCal.setTime(startDate);
			
			int day = startCal.get(Calendar.DAY_OF_WEEK);
			if (1 < day && 7 > day) {
				if (haveHolidays) {
					if (!Holidays.getInstance().isHolidayByDate(country, startDate)){
						lstDate.add(startDate);
					}
				} else {
					lstDate.add(startDate);
				}
			}
			
			startCal.add(Calendar.DATE, 1);
			startDate = startCal.getTime();
		}
		
		return lstDate;
	}
	
	/**
	 * get all the month end days from start date to end date.
	 * @param start
	 * @param end
	 * @return
	 */
	public static List<Date> getMonthlyDate(String start, String end) {
		if (null == start || 0 == start.length() 
				|| null == end || 0 == end.length()) {
			return null;
		}
		
		Date startDate = null;
		Date endDate = null;
		try {
			startDate = new SimpleDateFormat("yyyy-MM-dd").parse(start);
			endDate = new SimpleDateFormat("yyyy-MM-dd").parse(end);
		} catch (ParseException e) {
			e.printStackTrace();
		}
		
		if (null == startDate || null == endDate || startDate.getTime() > endDate.getTime()) {
			return null;
		}
		
		List<Date> lstDate = new ArrayList<Date>();
		while (startDate.getTime() <= endDate.getTime()) {
			Date lastDay = getLastDayOfMonth(startDate);
			if (startDate.getTime() > lastDay.getTime() || endDate.getTime() < lastDay.getTime()) {
				break;
			}
			
			lstDate.add(lastDay);
			
			Calendar startCal = Calendar.getInstance();
			startCal.setTime(startDate);
			startCal.add(Calendar.MONTH, 1);
			startDate = startCal.getTime();
		}
		
		return lstDate;
	}
	
	/**
	 * get all the Friday days from start date to end date.
	 * @param start
	 * @param end
	 * @return
	 */
	public static List<Date> getWeeklyDate(String start, String end) {
		if (null == start || 0 == start.length() 
				|| null == end || 0 == end.length()) {
			return null;
		}
		
		Date startDate = null;
		Date endDate = null;
		try {
			startDate = new SimpleDateFormat("yyyy-MM-dd").parse(start);
			endDate = new SimpleDateFormat("yyyy-MM-dd").parse(end);
		} catch (ParseException e) {
			e.printStackTrace();
		}
		
		if (null == startDate || null == endDate || startDate.getTime() > endDate.getTime()) {
			return null;
		}
		
		List<Date> lstDate = new ArrayList<Date>();
		Date friday = getFridayOfWeek(startDate);
		if (friday.getTime() < startDate.getTime()) {
			Calendar fridayCal = Calendar.getInstance();
			fridayCal.setTime(friday);
			fridayCal.add(Calendar.DATE, 7);
			friday = fridayCal.getTime();
		}
		
		while (friday.getTime() <= endDate.getTime()) {
			lstDate.add(friday);
			Calendar fridayCal = Calendar.getInstance();
			fridayCal.setTime(friday);
			fridayCal.add(Calendar.DATE, 7);
			friday = fridayCal.getTime();
		}
		
		return lstDate;
	}
	
	/**
	 * return the 31-mode index of endDate, its origin is the month of startDate
	 * @param startDate
	 * @param endDate
	 * @return
	 */
	public static long getDayIndexOfMonth(Date startDate, Date endDate) {
		if (null == startDate || null == endDate 
				|| startDate.getTime() > endDate.getTime()) {
			return -1;
		}
		
		Calendar cal = Calendar.getInstance();
		cal.setTime(endDate);
		long index = getDistMonths(startDate, endDate) * 31 + cal.get(Calendar.DAY_OF_MONTH) - 1;
        
		return index;
	}
	
	/**
	 * how many months between startDate and endDate
	 * @param startDate
	 * @param endDate
	 * @return
	 */
	public static int getDistMonths(Date startDate, Date endDate) {
		if (null == startDate || null == endDate) {
			return -1;
		}
		
		int diff = -1;
		Calendar startCal = Calendar.getInstance();
		startCal.setTime(startDate);
		Calendar endCal = Calendar.getInstance();
		endCal.setTime(endDate);
		int yearDiff = Math.abs((endCal.get(Calendar.YEAR) - startCal.get(Calendar.YEAR)));
		int monthDiff = Math.abs((endCal.get(Calendar.MONTH) - startCal.get(Calendar.MONTH)));
		diff = yearDiff * 12 + monthDiff;
		
		return diff;
	}
	
	/**
	 * get the day diffs between startDate and endDate
	 * @param startDate
	 * @param endDate
	 * @return
	 */
	public static long getDistDates(Date startDate, Date endDate) {
		if (null == startDate || null == endDate) {
			return -1;
		}
		
		long diff = -1;
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(startDate);
		long timestart = calendar.getTimeInMillis();
		calendar.setTime(endDate);
		long timeend = calendar.getTimeInMillis();
		diff = Math.abs((timeend - timestart)) / (1000 * 60 * 60 * 24);
		
		return diff;
	}

	/**
	 * get the day diffs between data and 1900-01-01
	 * @param date
	 * @return
	 */
	public static long getDaysFromOrigin(Date date) {
		if (null == date) {
			return -1;
		}
		
		long diff = -1;
		long start = 0;
		long end = 0;
		long dw = 0;
		TimeZone tz = TimeZone.getTimeZone("GMT-6");
		
		Calendar startCal = Calendar.getInstance(tz);
		startCal = Calendar.getInstance(tz);
		startCal.clear();
		startCal.set(Calendar.YEAR, 1970);
		startCal.set(Calendar.MONTH, 0);
		startCal.set(Calendar.DAY_OF_MONTH, 1);
		
		Calendar tempCal = Calendar.getInstance();
		tempCal.setTime(date);
		Calendar endCal = Calendar.getInstance(tz);
		endCal.clear();
		endCal.set(Calendar.YEAR, tempCal.get(Calendar.YEAR));
		endCal.set(Calendar.MONTH, tempCal.get(Calendar.MONTH));
		endCal.set(Calendar.DAY_OF_MONTH, tempCal.get(Calendar.DAY_OF_MONTH));
			
		start = startCal.getTimeInMillis();
		end = endCal.getTimeInMillis();
		dw = end - start;
		
		diff = Math.abs((dw)) / (1000 * 60 * 60 * 24) + DEFAULT_DAYS_FROM_19000101;
		
		return diff; 
	}
	
	public static Date getYesterdayOf(Date date)
	{
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		cal.add(Calendar.DATE, -1);
		return cal.getTime();
	}
	
	public static int getWeekOfYear(Date currentDate, Calendar cal) {
		cal.setTime(currentDate);
		return cal.get(Calendar.WEEK_OF_YEAR);
	}

	
	public static void main(String[] args) throws Exception
	{
//		Date startDate = new SimpleDateFormat("yyyy-MM-dd").parse("2010-10-05");
//		Date endDate = new SimpleDateFormat("yyyy-MM-dd").parse("2010-11-18");
//		System.out.println(DateFrequencyProcess.getDayIndexOfMonth(startDate, endDate));
//		System.out.println(DateFrequencyProcess.getDistMonths(startDate, endDate));
//		System.out.println(DateFrequencyProcess.getDaysFromOrigin(startDate));
//		System.out.println(DateFrequencyProcess.getDistDates(startDate, endDate));
//		System.out.println(DateFrequencyProcess.getWeeklyDate("2010-10-05", "2010-11-18"));
//		System.out.println(DateFrequencyProcess.getMonthlyDate("2010-08-05", "2010-11-18"));
//		System.out.println(DateFrequencyProcess.getDailyDate("2010-06-15", "2010-11-18", true, "USA"));
//		
//		Calendar cal = Calendar.getInstance();
//		cal.setTime(new SimpleDateFormat("yyyy-MM-dd").parse("2010-11-07"));
//		System.out.println(cal.get(Calendar.DAY_OF_WEEK));
//		
//		System.out.println(DateFrequencyProcess.getYesterdayOf(new Date()));
//		
		SimpleDateFormat df = new SimpleDateFormat("yyyy=MM=dd");
		Calendar cal = Calendar.getInstance();
		cal.setTime(df.parse("2010=09=01"));
		for (int i = 0; i < 100; i++)
		{
			if (1 == cal.get(Calendar.DAY_OF_WEEK) || 7 == cal.get(Calendar.DAY_OF_WEEK))
			{
				System.out.println(df.format(cal.getTime()) + "*");
			}
			else
			{
				System.out.println(df.format(cal.getTime()));
			}
			cal.add(Calendar.DATE, 1);
		}
		//System.out.println(DateFrequencyProcess.getWeekOfYear(new Date(), Calendar.getInstance()));
	}
	
	
	
	
}
